﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PlayerTest.Player.Event;
using System.Windows.Forms;
using DirectShowLib;
using System.Threading;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.IO;
using PlayerTest.Player.Data;
using mshtml;



namespace PlayerTest.Player
{
    class SubtitleHandler
    {
        #region Member Variables

        string typeSMI = null;
        
        List<SMIContent> KRCC_SMI = new List<SMIContent>();
        List<SMIContent> ENCC_SMI = new List<SMIContent>();

        public List<SMIContent> DIC_KRCC
        {
            get { return KRCC_SMI; }
            set { KRCC_SMI = value; }
        }
        public List<SMIContent> DIC_ENCC
        {
            get { return ENCC_SMI; }
            set { ENCC_SMI = value; }
        }

        public string TypeSMI
        {
            get { return typeSMI; }
            set { typeSMI = value; }
        }

        // 자막 파싱 스레드에서 메인 스레드 접근
        private SynchronizationContext _threadContext;
        public CorePlayer player;
        public double Position
        {
            get{ return player.CurrentPosition; }
        }

        // 자막 모니터링 스레드
        private Thread KRCCsubtitleMonitor;
        private Thread ENCCsubtitleMonitor;

        #endregion

        public SubtitleHandler()
        {
            // 메인 스레드 설정
            _threadContext = SynchronizationContext.Current;
        }

        ~SubtitleHandler()
        {
            SubtitleMonitoringStop();
        }

        #region 이벤트 처리 / 전송
        public event SubtitleEventHandler EventHandler;
        /// <summary>
        /// Player에게 이벤트 전송
        /// </summary>
        /// <param name="arg"></param>
        private void SendSubtitleEvent(SubtitleEventArgs arg)
        {
            // 연결된 모든 함수에 이벤트 전송
            SubtitleEventHandler handler = EventHandler;
            if (handler != null) handler(this, arg);
        }
        public void test()
        {
            // Controller로 이벤트 보내기
            SubtitleEventArgs args = new SubtitleEventArgs(SubtitleEventArgs.eventtype.MOVE);
            
            // 이벤트 전달값 설정
            args.Position = 1.0f;

            // 이벤트 전송
            SendSubtitleEvent(args);
        }

        /// <summary>
        /// Thread로부터 이벤트를 받아 처리할 함수
        /// </summary>
        /// <param name="state"></param>
        private void OnThreadPost(object state)
        {
            if (state != null)
            {
                bool result = (bool)state;

                // 파싱 결과 전송
                SubtitleEventArgs arg = new SubtitleEventArgs(SubtitleEventArgs.eventtype.PARSEFINISH);
                arg.Result = result;
                SendSubtitleEvent(arg);
            }
        }
        #endregion

        #region 자막 파싱
        /// <summary>
        /// 자막을 열어서 파싱합니다.(비동기)
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public void OpenSubtitle(string path)
        {
            // 진행중인 자막이 있다면 중지
            SubtitleMonitoringStop();

            //bool result = false;
            // 자막파일
            FileInfo fi = new FileInfo(path);
            if (fi.Exists)
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(SubtitleParsingThread), path);
                //result = ParsingSMI(path, true);
            }
        }

        /// <summary>
        /// 자막을 독립 스레드에서 파싱하여 저장합니다.
        /// </summary>
        /// <param name="o"></param>
        private void SubtitleParsingThread(object o)
        {
            string path = o as string;
            if (path != null)
            {
                // 파싱
                bool result = ParsingSMI(path, true);

                _threadContext.Post(OnThreadPost, result);
            }
        }

        /// <summary>
        /// SMI 자막 파싱하여 저장
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="isClickOepnSmi"></param>
        private bool ParsingSMI(string filePath, bool isClickOepnSmi)
        {
            //Encoding.UTF8 || Encoding.Default
            bool result = false;
            KRCC_SMI.Clear();
            ENCC_SMI.Clear();

            try
            {
                StreamReader reader = new StreamReader(new FileStream(filePath, FileMode.Open), Encoding.Default);

                string allText = reader.ReadToEnd();

                HTMLDocument doc = new HTMLDocument();
                doc.designMode = "on";

                object[] oPageText = { allText };
                IHTMLDocument2 oMyDoc = (IHTMLDocument2)doc;
                oMyDoc.write(oPageText);

                int i = 0, doc_size = 0;
                int encc_key = 0, krcc_key = 0;
                long time;
                string type = null, txt = null;

                doc_size = doc.getElementsByTagName("SYNC").length;


                IHTMLElement element = (IHTMLElement)doc.getElementsByTagName("P").item(0);
                type = element.getAttribute("className");
                element = (IHTMLElement)doc.getElementsByTagName("P").item(doc_size - 1);

                /*
                if (isClickOepnSmi)
                {
                    typeSMI = SeperateSMI(type, element.getAttribute("className"));
                    isClickOepnSmi = false;
                }
                */

                while (i < doc_size)
                {
                    element = (IHTMLElement)doc.getElementsByTagName("SYNC").item(i);
                    time = long.Parse(element.getAttribute("start"));

                    element = (IHTMLElement)doc.getElementsByTagName("P").item(i);
                    type = element.getAttribute("className");
                    txt = element.innerText;

                    // 한글이 KRCC가 아니라면 어떨까?
                    // KRCC / Korean 등등 자막제작자들이 멋대로 입력하는 경우가 많음
                    // krcc kor ko korean
                    switch (type.ToLower())
                    {
                        case "krcc":
                            KRCC_SMI.Add(new SMIContent(time, type, txt));
                            krcc_key++;
                            break;
                        case "kor":
                            KRCC_SMI.Add(new SMIContent(time, type, txt));
                            krcc_key++;
                            break;
                        case "ko":
                            KRCC_SMI.Add(new SMIContent(time, type, txt));
                            krcc_key++;
                            break;
                        case "korean":
                            KRCC_SMI.Add(new SMIContent(time, type, txt));
                            krcc_key++;
                            break;
                        default:
                            ENCC_SMI.Add(new SMIContent(time, type, txt));
                            encc_key++;
                            break;
                    }
                    /*
                    else if (typeSMI.Equals("UNITED"))
                    {
                        dic_SMI.Add(key, new SMIContent(time, type, txt));
                        key++;
                    }
                    */
                    i++;
                }
                reader.Close();
                result = true;
            }
            catch (Exception e)
            {
                Trace.WriteLine(e.StackTrace);
            }
            return result;
        }

        private string SeperateSMI(string first, string last)
        {
            if (!first.Equals(last))
            {
                return "UNITED";
            }
            else
            {
                if (first.Equals("KRCC"))
                    return "KRCC";
                else
                    return "ENCC";
            }
        }
        #endregion

        #region 자막 모니터링
        private int startIndexKRCC;
        private int startIndexENCC;
        private string subKRCC;
        private string subENCC;
        private PlayerTest.CorePlayer.SubType currentSubType;
        public string KRCC
        {
            get { return subKRCC; }
        }
        public string ENCC
        {
            get { return subENCC; }
        }

        #region Monitor Control
        public void SubtitleMonitoringStart()
        {
            SubtitleMonitoringStart( currentSubType );
        }
        public void SubtitleMonitoringStart(PlayerTest.CorePlayer.SubType subtype)
        {
            currentSubType = subtype;
            // KRCC
            if (subtype == PlayerTest.CorePlayer.SubType.KRCC ||
                subtype == PlayerTest.CorePlayer.SubType.UNITED)
            {
                if (KRCCsubtitleMonitor != null)
                {
                    if (KRCCsubtitleMonitor.ThreadState == System.Threading.ThreadState.Suspended)
                        KRCCsubtitleMonitor.Resume();
                    else
                    {
                        KRCCsubtitleMonitor = new Thread(new ThreadStart(Monitor_KRCC));
                        KRCCsubtitleMonitor.Start();
                    }
                }
            }

            // ENCC
            if (subtype == PlayerTest.CorePlayer.SubType.ENCC ||
                subtype == PlayerTest.CorePlayer.SubType.UNITED)
            {
                if (ENCCsubtitleMonitor != null)
                {
                    if (ENCCsubtitleMonitor.ThreadState == System.Threading.ThreadState.Suspended)
                        ENCCsubtitleMonitor.Resume();
                    else
                    {
                        ENCCsubtitleMonitor = new Thread(new ThreadStart(Monitor_KRCC));
                        ENCCsubtitleMonitor.Start();
                    }
                }
            }
        }
        public void SubtitleMonitoringPause()
        {
            // KRCC
            if (KRCCsubtitleMonitor != null)
                KRCCsubtitleMonitor.Suspend();

            // ENCC
            if (ENCCsubtitleMonitor != null)
                ENCCsubtitleMonitor.Suspend();
        }
        public void SubtitleMonitoringStop()
        {
            // KRCC
            if (KRCCsubtitleMonitor != null)
            {
                KRCCsubtitleMonitor.Abort();
                KRCCsubtitleMonitor = null;
            }

            // ENCC
            if (ENCCsubtitleMonitor != null)
            {
                ENCCsubtitleMonitor.Abort();
                ENCCsubtitleMonitor = null;
            }
        }
        public void SubtitleMonitoringMove()
        {
            if (KRCCsubtitleMonitor != null)
            {
                KRCCsubtitleMonitor.Abort();
            }
            KRCCsubtitleMonitor = new Thread(new ThreadStart(Monitor_KRCC));
            KRCCsubtitleMonitor.Start();

            if (ENCCsubtitleMonitor != null)
            {
                ENCCsubtitleMonitor.Abort();
            }
            ENCCsubtitleMonitor = new Thread(new ThreadStart(Monitor_ENCC));
            ENCCsubtitleMonitor.Start();
        }
        #endregion

        #region KRCC Monitor
        private void Monitor_KRCC()
        {
            int subtitleIndex = searchKRCCIndex();
            double pos = 0.0;
            //Trace.WriteLine("[SUB] 자막 모니터링 시작.");
            _threadContext.Post(player.SetKRCC, "");
            while (KRCC_SMI.Count>0)
            {
                pos = Position*1000.0;

                // 다음 자막까지 대기
                while (pos < KRCC_SMI[subtitleIndex].Time)
                {
                    Thread.Sleep(50);
                    pos = Position * 1000.0;
                }

                subKRCC = KRCC_SMI[subtitleIndex].Text;
                subtitleIndex = (subtitleIndex + 1 < KRCC_SMI.Count) ? subtitleIndex + 1 : subtitleIndex;
                //Trace.WriteLine( string.Format("[SUB] {0}", subKRCC));

                _threadContext.Post(player.SetKRCC, subKRCC);
            }
        }
        
        private int searchKRCCIndex()
        {
            int index = 0;
            double current = Position * 1000.0;

            for (int i = 0; i < KRCC_SMI.Count; i++)
            {
                if (KRCC_SMI[i].Time > current)
                {
                    index = i;
                    break;
                }
            }
            return index;
        }
        #endregion

        #region ENCC Monitor
        private void Monitor_ENCC()
        {
            int subtitleIndex = searchENCCIndex();
            double pos = 0.0;
            //Trace.WriteLine("[SUB] 자막 모니터링 시작.");
            _threadContext.Post(player.SetENCC, "");
            while (ENCC_SMI.Count > 0)
            {
                pos = Position * 1000.0;

                // 다음 자막까지 대기
                while (pos < ENCC_SMI[subtitleIndex].Time)
                {
                    Thread.Sleep(50);
                    pos = Position * 1000.0;
                }

                subENCC = ENCC_SMI[subtitleIndex].Text;
                subtitleIndex = (subtitleIndex + 1 < ENCC_SMI.Count) ? subtitleIndex + 1 : subtitleIndex;
                //Trace.WriteLine( string.Format("[SUB] {0}", subKRCC));

                _threadContext.Post(player.SetENCC, subENCC);
            }
        }

        private int searchENCCIndex()
        {
            int index = 0;
            double current = Position * 1000.0;

            for (int i = 0; i < ENCC_SMI.Count; i++)
            {
                if (ENCC_SMI[i].Time > current)
                {
                    index = i;
                    break;
                }
            }
            return index;
        }
        #endregion

        #endregion
    }
}